#include "Block.h"
#include "MainState.h"
#include "RandomNumberGenerator.h"
#define _USE_MATH_DEFINES
#include <math.h>

bool FindSplitPoint(Triangle Tri,D3DXVECTOR3& Split,D3DXVECTOR3& point)
{

	if(Tri.a == Tri.b || Tri.a == Tri.c || Tri.c == Tri.b)
	{
		return false;
	}
	float t; //distance along the line
	D3DXVECTOR3 P0,P1;//points on the line 
	D3DXVECTOR3 M; //the line
	D3DXVECTOR3 Q; //the corner
	D3DXVECTOR3 P; //Closest point

	P0=Tri.a;
	P1=Tri.b;
	Q=Tri.c;

	M=P1-P0;
	t=D3DXVec3Dot(&M,&(Q-P0))/D3DXVec3Dot(&M,&M);
	if(t>0&&t<1)
	{
		point=Q;
		Split= P0+(t*(P1-P0));
		return true;
	}

	P0=Tri.b;
	P1=Tri.c;
	Q=Tri.a;

	M=P1-P0;
	t=D3DXVec3Dot(&M,&(Q-P0))/D3DXVec3Dot(&M,&M);
	if(t>0&&t<1)
	{
		point=Q;
		Split= P0+(t*(P1-P0));
		return true;
	}

	P0=Tri.c;
	P1=Tri.a;
	Q=Tri.b;

	M=P1-P0;
	t=D3DXVec3Dot(&M,&(Q-P0))/D3DXVec3Dot(&M,&M);
	if(t>0&&t<1)
	{
		point=Q;
		Split= P0+(t*(P1-P0));
		return true;
	}
//	MessageBox(0,L"Split Point fail",L"Error",MB_OK);
	return false;
	
}

float TriArea(Triangle t)
{
	D3DXVECTOR3 corner, split, base, height;
	float res =-1;
	if(FindSplitPoint(t,split,corner))
	{
		
		height = corner-split;
		if(corner==t.a)
		{
			base =t.bc;
		}
		else if(corner==t.b)
		{
			base =t.ac;
		}
		else if(corner==t.c)
		{
			base =t.ab;
		}
		else
		{
			MessageBox(0,L"Not a corner",L"Error",MB_OK);
		}
		
		res = (D3DXVec3Length(&base)*D3DXVec3Length(&height))/2;
	}
	return res;
}
bool Block::Create(std::vector<D3DXVECTOR3> v,D3DXVECTOR3 centre)
{
	HRESULT hr;
	m_vCorners = v;
	int NumTris = v.size();
	int NumVerts =v.size()+1;
	m_vPos=centre;

	for (unsigned int i=0;i<v.size();++i)
	{
		m_vCorners[i]-=(*D3DXVec3Normalize(&D3DXVECTOR3(0,0,0),&(m_vCorners[i]-m_vPos)));
	}
	V(D3DXCreateMesh(NumTris,NumVerts,D3DXMESH_MANAGED,TheD3DApp::Instance()->GetVertexElements(),TheD3DApp::Instance()->GetD3DDev(),&m_pMesh));

	POSNORMUVVERTEX* pVertices;
	m_pMesh->LockVertexBuffer(0, (void**)&pVertices);

	pVertices[0].Pos=D3DXVECTOR3(0,0,0);
	pVertices[0].Norm=D3DXVECTOR3(0,1,0);
	pVertices[0].UV=D3DXVECTOR2(0,0);

	for (unsigned int i=0;i<v.size();++i)
	{
		pVertices[i+1].Pos=m_vCorners[i]-m_vPos;
		pVertices[i+1].Norm=D3DXVECTOR3(0,1,0);
		pVertices[i+1].UV=D3DXVECTOR2(0,0);
	}
	m_pMesh->UnlockVertexBuffer();


	WORD *IndexData=NULL;
	V(m_pMesh->LockIndexBuffer( 0, (void**)&IndexData ));

	int indCount=0;
	for(unsigned int i=0; i<NumTris;++i)
	{
		IndexData[indCount++]=0;
		IndexData[indCount++]=i+1;
		if(i<NumTris-1)
		{
			IndexData[indCount++]=i+2;
		}
		else
		{
			IndexData[indCount++]=1;
		}
	}
	m_pMesh->UnlockIndexBuffer();

	for (unsigned int i=0;i<m_vCorners.size();++i)
	{
		Triangle t;
		if(i<m_vCorners.size()-1)
		{t=Triangle(D3DXVECTOR3(0,0,0),m_vCorners[i]-m_vPos,m_vCorners[i+1]-m_vPos);}
		else
		{t=Triangle(D3DXVECTOR3(0,0,0),m_vCorners[i]-m_vPos,m_vCorners[0]-m_vPos);}
		m_vRawTris.push_back(t);
	}

	while(!m_vRawTris.empty())
	{
		for (unsigned int i=0; i<m_vRawTris.size();++i)
		{
			D3DXVECTOR3 point;
			D3DXVECTOR3 splitPoint;
			bool TriOK = FindSplitPoint(m_vRawTris[i],splitPoint,point);
			if(TriOK)
			{
			float area = TriArea(m_vRawTris[i]);
			
			//cut this tri into 2 right angles
			if(area>50)
			{
				Triangle t1,t2;
				//note that a is always the right angle
				if(point==m_vRawTris[i].a)
				{
					t1= Triangle(splitPoint,m_vRawTris[i].b,point);
					t2=Triangle(splitPoint,m_vRawTris[i].c,point);
				}
				if(point==m_vRawTris[i].b)
				{
					t1= Triangle(splitPoint,m_vRawTris[i].c,point);
					t2=Triangle(splitPoint,m_vRawTris[i].a,point);
				}
				if(point==m_vRawTris[i].c)
				{
					t1= Triangle(splitPoint,m_vRawTris[i].a,point);
					t2=Triangle(splitPoint,m_vRawTris[i].b,point);
				}

				float area1 = TriArea(t1);
				float area2 = TriArea(t1);
				if( area1>10 ||  area2>10)
				{	
					m_vPossibles.push_back(t1);
					m_vPossibles.push_back(t2);
				}
				else
				{
					//irregular
					area =0;
					//divide triangle into tri and quad

				}

			}
			//try and use as is
			else if(area>5)
			{
				Building* b=new Building;
				b->Generate(m_vPos,m_vRawTris[i],30);
				m_pvBuildings.push_back(b);
				MainState::Instance()->addGameObject(b);
			}
			//if neither of these can be done, ignore this triangle
			}
		}
		m_vRawTris.clear();
		if(m_vPossibles.size()>1000)
		{
			MessageBox(0,L"too many possibles",L"Error",MB_OK);
			m_vPossibles.clear();
			return false;
		}
		for (unsigned int i=0; i<m_vPossibles.size();++i)
		{
			D3DXVECTOR3 point;
			D3DXVECTOR3 splitPoint;
			bool TriOK = FindSplitPoint(m_vPossibles[i],splitPoint,point);
			if(TriOK)
			{
			float area = TriArea(m_vPossibles[i]);

			//if small enough to use
			if(area<50 && area>10)
			{
				m_vBuildAreas.push_back(m_vPossibles[i]);
			}

			//send back to raw tris
			else
			{
				m_vRawTris.push_back(m_vPossibles[i]);
			}
		}
		}
		m_vPossibles.clear();
		if(m_vBuildAreas.size()>1000)
		{
			MessageBox(0,L"too many Build Areas",L"Error",MB_OK);
			m_vPossibles.clear();
			return false;
		}
	}

	//find where a building will fit
	for (unsigned int i=0;i<m_vBuildAreas.size();++i)
	{
		D3DXVECTOR3 BuildingSize, BuildingPos,point;
		bool TriOK=false;
		D3DXVECTOR3 Q;
		D3DXVECTOR3 QA;
		float length =D3DXVec3Length(&m_vBuildAreas[i].bc);
		//if(length>3)
		{
			TriOK=true;
			//find an acceptable point on line bc
			float f = MY_RAND(length/2)+length/4;
			float scale= f/length;
			
			point = m_vBuildAreas[i].a;
			Q = (m_vBuildAreas[i].b+(m_vBuildAreas[i].bc*scale));
			if(point!=m_vBuildAreas[i].a)
			{
				MessageBox(0,L"wrong point in triangle division", L"Error",MB_OK);
			}
			
			float h = D3DXVec3Length(&(Q-point));
			D3DXVec3Normalize(&QA,&(point - Q));

			BuildingSize.x=h*cos(D3DXVec3Dot(&m_vBuildAreas[i].NormCA,&QA));
			BuildingSize.z=h*cos(D3DXVec3Dot(&m_vBuildAreas[i].NormBA,&QA));
			BuildingSize.y=60;
		}
		if(TriOK)
		{
			if(fabs(BuildingSize.x)>2 && fabs(BuildingSize.z)>2)
			{
			
				double angle = acos(D3DXVec3Dot(D3DXVec3Normalize(&D3DXVECTOR3(),&m_vBuildAreas[i].ac),&D3DXVECTOR3(1,0,0)));
  				//angle = fmod(angle,M_PI_2);
 				BuildingPos=m_vPos+point+((Q-point)/2);
				BuildingPos.y=min(m_vBuildAreas[i].a.y,min(m_vBuildAreas[i].b.y,m_vBuildAreas[i].c.y));
				Building* b =new Building;
				//b->Generate(BuildingPos,D3DXVECTOR3(1,10,3));
				b->Generate(BuildingPos,D3DXVECTOR3(fabs(BuildingSize.x),BuildingSize.y,fabs(BuildingSize.z)));
				b->SetRotY(angle);
				m_pvBuildings.push_back(b);
				MainState::Instance()->addGameObject(b);

			}
		}
	}


	V(D3DXComputeNormals(m_pMesh,0));
	return true;
}

void Block::Render()
{
	GameObject::Render();

	if(m_bDraw)
	{
		D3DXMATRIX mw,mv,mp,mvp;
		TheD3DApp::Instance()->GetD3DDev()->GetTransform(D3DTS_VIEW,&mv);
		TheD3DApp::Instance()->GetD3DDev()->GetTransform(D3DTS_PROJECTION,&mp);
		mvp= mv*mp;
		LPD3DXLINE l;
		D3DXCreateLine(TheD3DApp::Instance()->GetD3DDev(),&l);
		for (unsigned int i=0; i < m_vBuildAreas.size();++i)
		//for (unsigned int i=0; i < 1;++i)
		{
			D3DXVECTOR3 points[4];
			points[0] = m_vBuildAreas[i].a+m_vPos;
			points[1] = m_vBuildAreas[i].b+m_vPos;
			points[2] = m_vBuildAreas[i].c+m_vPos;
			points[3] = m_vBuildAreas[i].a+m_vPos;


			l->DrawTransform(points,4,&mvp,D3DCOLOR_RGBA(255,0,0,255));

		}
	}
}